#include <cassert>

#include "math/vec2.h"
#include "math/utility.h"

#ifdef __CPLUSPLUS

	slVec2::slVec2() {
		m_X = 0.0f;
		m_Y = 0.0f;
	}

	slVec2::slVec2(float x, float y) {
		m_X = x;
		m_Y = y;
	}

	slVec2::slVec2(const slVec2& vec) {
		m_X = vec.m_X;
		m_Y = vec.m_Y;
	}

	inline slScalar slVec2::Length() {
		return slVec2Length(this);
	}

/*
	// casting
	operator slScalar* slVec2::operator() {
		return reinterpret_cast<slScalar*> (this);
	}

	operator const slScalar* slVec2::() const {
		return reinterpret_cast<const slScalar*> (this);
	}*/

	slVec2& slVec2::operator+= (const slVec2& vec) {
		slVec2Add(this, this, &vec);
		return *this;
	}

	slVec2& slVec2::operator-= (const slVec2& vec) {
		slVec2Subtract(this, this, &vec);
		return *this;
	}

	slVec2& slVec2::operator*= (slScalar s) {
		slVec2Scale(this, this, s);
		return *this;
	}

	slVec2& slVec2::operator/= (slScalar s) {
		assert(0); //TODO: Finish this
	}

	slVec2 slVec2::operator+ () const {
		assert(0);
	}

	slVec2 slVec2::operator- () const {
		return slVec2(-m_X, -m_Y);
	}

	slVec2 slVec2::operator+ (const slVec2& vec) const {
		slVec2 ret;
		slVec2Add(&ret, this, &vec);
		return ret;
	}

	slVec2 slVec2::operator- (const slVec2& vec) const {
		slVec2 ret;
		slVec2Subtract(&ret, this, &vec);
		return ret;
	}

	slVec2 slVec2::operator* (slScalar s) const {
		slVec2 ret;
		slVec2Scale(&ret, this, s);
		return ret;
	}

	slVec2 slVec2::operator/ (slScalar s) const {
		assert(0);
	}

	bool slVec2::operator== (const slVec2& vec) const {
		return slVec2AreEqual(this, &vec);
	}

	bool slVec2::operator!= (const slVec2& vec) const {
		return !slVec2AreEqual(this, &vec);
	}
	
#endif

slScalar slVec2Length(const slVec2* pIn)
{
    return slSqrt(slSQR(pIn->m_X) + slSQR(pIn->m_Y));
}

slScalar slVec2LengthSq(const slVec2* pIn)
{
    return slSQR(pIn->m_X) + slSQR(pIn->m_Y);
}

slVec2* slVec2Normalize(slVec2* pOut, const slVec2* pIn)
{
	slScalar l = 1.0 / slVec2Length(pIn);

	pOut->m_X *= l;
	pOut->m_Y *= l;

	return pOut;
}

slVec2* slVec2Add(slVec2* pOut, const slVec2* pV1, const slVec2* pV2)
{
	pOut->m_X = pV1->m_X + pV2->m_X;
	pOut->m_Y = pV1->m_Y + pV2->m_Y;

	return pOut;
}

slVec2* slVec2Subtract(slVec2* pOut, const slVec2* pV1, const slVec2* pV2)
{
	pOut->m_X = pV1->m_X - pV2->m_X;
	pOut->m_Y = pV1->m_Y - pV2->m_Y;

	return pOut;
}

slVec2* slVec2Transform(slVec2* pOut, const slVec2* pV1, const slMat4* pM)
{
	assert(0);
}

slVec2* slVec2TransformCoord(slVec2* pOut, const slVec2* pV, const slMat4* pM)
{
	assert(0);
}

slVec2* slVec2Scale(slVec2* pOut, const slVec2* pIn, const slScalar s)
{
	assert(0);
}

bool slVec2AreEqual(const slVec2* p1, const slVec2* p2)
{
	return (
				(p1->m_X < p2->m_X + slEpsilon && p1->m_X > p2->m_X - slEpsilon) &&
				(p1->m_Y < p2->m_Y + slEpsilon && p1->m_Y > p2->m_Y - slEpsilon)
			);
}
